﻿#include "stdafx.h"
#ifdef LK_WINDOWS_WAS_SPEAKER_
namespace LK
{
	namespace Windows
	{
		using namespace ::LK::Sound;
		namespace WAS
		{
			Speaker::Speaker()
				:hz(DefaultHz), ch(DefaultChannels), bits(DefaultBits), isFloat(false),//设置默认格式
				isPlaying(false), started(false), isSelecting(false), isWriting(false),
				pDevice(NULL),
				DeviceID(ptr<String>::GetEmpty()), DeviceName(ptr<String>::GetEmpty()),
				pAudioClient(NULL), pRenderClient(NULL),
				SpeakingFormat(),
				hnsRequestedDuration(SoundEnvironment::GetDefaultRef().DefaultSpeakerRequestedDuration),
				converter(),
				isClosing(false),
				UsingDefaultDevice(true)
			{
				buffer.SetEnableThreadSafe(true);
				SelectDeviceById(ptr<String>::GetEmpty());
				SoundEnvironment::GetDefaultRef().AddSpeaker(this);
			}
			Speaker::~Speaker()
			{
				SoundEnvironment::GetDefaultRef().RemoveSpeaker(this);
				isClosing = true;
				tc.Lock();
				Stop();
				DeviceName = ptr<String>::GetEmpty();
				if (pRenderClient)pRenderClient->Release();
				pRenderClient = NULL;
				if (pAudioClient)pAudioClient->Release();
				pAudioClient = NULL;
				if (pDevice)pDevice->Release();
				pDevice = NULL;
				tc.UnLock();
				index_t i = 0;
				while ((isWriting || isSelecting) && i++ < 50){ isClosing = true; Sleep(10); }
			}
			void Speaker::Close()
			{
				tc.Lock();
				isClosing = true;
				Stop();
				DeviceName = ptr<String>::GetEmpty();
				DeviceID = ptr<String>::GetEmpty();
				if (pRenderClient)pRenderClient->Release();
				pRenderClient = NULL;
				if (pAudioClient)pAudioClient->Release();
				pAudioClient = NULL;
				if (pDevice)pDevice->Release();
				pDevice = NULL;
				isClosing = false;
				tc.UnLock();
			}
			void Speaker::Start()
			{
				while (!tc.Try())
				{
					if (isClosing)return;
					Sleep(1);
				}
				if (pAudioClient)
				{
					pAudioClient->Start();
					isPlaying = true;
					started = true;
				}
				tc.UnLock();
			}
			void Speaker::Stop()
			{
				while (!tc.Try())
				{
					if (isClosing)return;
					Sleep(1);
				}
				if (isPlaying && pAudioClient)
				{
					isPlaying = false;
					started = false;
					pAudioClient->Stop();
					buffer.Reset();
				}
				tc.UnLock();
			}
			//清空缓存内容
			void Speaker::Clear()
			{
				while (!tc.Try()){ if (isClosing)return; }
				buffer.Reset();
				tc.UnLock();
			}
			void Speaker::Pause()
			{
				while (!tc.Try())
				{
					if (isClosing)return;
					Sleep(1);
				}
				if (pAudioClient)
				{
					isPlaying = false;
					pAudioClient->Stop();
				}
				tc.UnLock();
			}
			void Speaker::SetFormat(double h, short bit, short channel, bool isfloat)
			{
				if (h <= 0 || this->hz == h && this->ch == channel && this->bits == bit && this->isFloat == isfloat)return;
				tc.Lock();
				buffer.Reset();
				this->hz = h;
				this->ch = channel;
				this->bits = bit;
				this->isFloat = isfloat;
				converter.SetFormat(h, bit, channel, isfloat);
				tc.UnLock();
				return;
			}
			void Speaker::SetFormat(const WAVEFORMATEX& pwfx)
			{
				SetFormat(pwfx.nSamplesPerSec, pwfx.wBitsPerSample, pwfx.nChannels, pwfx.wFormatTag == 0xfffe);
			}
			void Speaker::SetToDefaultFormat()
			{
				SetFormat(DefaultHz, DefaultBits, DefaultChannels);
			}
			void Speaker::GetFormat(index_t* phz, short* pbits, short* pch, bool* pIsfloat)
			{
				if (phz)*phz = (index_t)hz;
				if (pch)*pch = ch;
				if (pbits)*pbits = bits;
				if (pIsfloat)*pIsfloat = isFloat;
			}
			WAVEFORMATEX Speaker::GetFormat()
			{
				index_t h; short bit; short channel; bool isfloat;
				GetFormat(&h, &bit, &channel, &isfloat);
				return Wave::MakeFormat((double)h, bit, channel, isfloat);
			}
			void Speaker::SetVol(double vol)
			{
				converter.Volume = vol;
			}
			double Speaker::GetVol()const
			{
				return converter.Volume;
			}
			void Speaker::SetBufferTime(const ptr<Time>& hus)
			{
				ptr<Time> hms(hus);
				if (hms < SoundEnvironment::GetDefaultRef().MinimumSpeakerRequestedDuration)//限制
					hms = SoundEnvironment::GetDefaultRef().MinimumSpeakerRequestedDuration;
				if (hms == hnsRequestedDuration)return;
				hnsRequestedDuration = hms;
				bool pa = started && !isPlaying;
				SelectDeviceById(DeviceID);
				if (pa)Pause();
			}
			bool Speaker::SelectDeviceById(const ptr<String>& id)
			{
				HRESULT hr = 0;
				isSelecting = true;
				while (!tc.Try())
				{
					if (isClosing)
					{
						isSelecting = false;
						return false;
					}
					Sleep(1);
				}
				Close();//关闭，重新建立会话
				if (pDevice = id.IsNullOrEmpty() ? SoundEnvironment::GetDefaultRef().GetDefaultSpeakingDevice() : SoundEnvironment::GetDefaultRef().GetDevice(id))//获取设备成功
				{
					DeviceID = id;
					DeviceName = SoundEnvironment::GetDefaultRef().GetDeviceName(id);//设备名称
					if (SUCCEEDED(pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))//激活会话
					{
						//获取混音格式
						WAVEFORMATEX* pwfx;
						if (FAILED(hr = pAudioClient->GetMixFormat(&pwfx)))goto End;
						SpeakingFormat = *pwfx;
						//使用共享模式，按hnsRequestedDuration的时间间隔进行初始化
						if (FAILED(hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, (REFERENCE_TIME)((double)hnsRequestedDuration*10000000), 0, pwfx, NULL)))goto End;
						CoTaskMemFree(pwfx);
						//获取块帧数
						if (FAILED(hr = pAudioClient->GetBufferSize(&bufferFrameCount)))goto End;
						//获取播放服务
						if (FAILED(hr = pAudioClient->GetService(IID_IAudioRenderClient, (void**)&pRenderClient)))goto End;
					}
					isSelecting = false;
					tc.UnLock();
					return true;
				}
			End:;
				isSelecting = false;
				if (FAILED(hr))
				{
					Close();
					tc.UnLock();
					Throw<PSoundException >(hr);
				}
				tc.UnLock();
				return false;
			}
			bool Speaker::SelectDeviceByName(const ptr<String>& name)
			{
				return SelectDeviceById(SoundEnvironment::GetDefaultRef().GetDeviceID(name));
			}
			void Speaker::Restart()
			{
				bool pa = started && !isPlaying;
				SelectDeviceById(DeviceID);
				if (pa)Pause();
				else Start();
			}
			//设备添加事件
			void Speaker::OnDeviceAdded()
			{
				try{
					if (SoundEnvironment::GetDefaultRef().OnAddedDeviceId == this->DeviceID)Restart();
				}
				catch (...)
				{
					Sleep(500);
					try{
						SelectDeviceById(SoundEnvironment::GetDefaultRef().OnAddedDeviceId);
					}
					catch (...)
					{
					}
				}
			}
			//设备状态改变事件
			void Speaker::OnDeviceStateChanged()
			{

			}
			//设备移除事件
			void Speaker::OnDeviceRemoved()
			{
				try{
					if (SoundEnvironment::GetDefaultRef().OnRemovedDeviceId == this->DeviceID)Close();
				}
				catch (...){}
			}
			//默认设备改变事件
			void Speaker::OnDefaultDeviceChanged()
			{
				try{
					if (UsingDefaultDevice && SoundEnvironment::GetDefaultRef().OnDefaultChangedFlow == eRender)SelectDeviceById(SoundEnvironment::GetDefaultRef().OnDefaultChangedDeviceId);
				}
				catch (...)
				{
					Sleep(500);
					try{
						if (UsingDefaultDevice && SoundEnvironment::GetDefaultRef().OnDefaultChangedFlow == eRender)SelectDeviceById(SoundEnvironment::GetDefaultRef().OnDefaultChangedDeviceId);
					}
					catch (...){}
				}
			}
			//设备属性改变事件
			void Speaker::OnDevicePropertyValueChanged()
			{
			}
			void Speaker::WriteByte(u8* p, size_t len)
			{
				HRESULT hr = 0;
				if (!pRenderClient)return;
				while (!tc.Try()) { if (isClosing)return; Sleep(1); }
				isWriting = true;
				LK::Collection::ReferenceArray<u8> pd(len, p);
				PByteArray pdata;
				if (!pRenderClient)goto End;
				UINT32 frames;
				unsigned char *pdes;
				//转换数据格式
				pdata = converter.Convert<u8>(&pd, SpeakingFormat);//获得转换后的新内存和单声道帧数量
				if ((index_t)buffer.GetLength() < pdata->GetLengthOfBytes() + (index_t)buffer.GetUnreadSize())buffer.Resize(pdata->GetLengthOfBytes() + buffer.GetUnreadSize());//调整大小
				if ((size_t)buffer.GetLength() < bufferFrameCount*SpeakingFormat.nBlockAlign)buffer.Resize(bufferFrameCount*SpeakingFormat.nBlockAlign);
				//可填充数据先聚集到buffer中
				buffer.Write(pdata);

				//buffer中可填充数据少于总可填充空间的一半时将直接返回
				if (buffer.GetUnreadSize() < bufferFrameCount*(SpeakingFormat.nBlockAlign / 2))
					goto End;

				while (pAudioClient)
				{
					//获取已填充帧数
					if (FAILED(hr = pAudioClient->GetCurrentPadding(&frames)))goto End;
					frames = bufferFrameCount - frames;//获取可写入空间
					if (frames < (bufferFrameCount / 2))//可填充空间过少，则需要播放以消耗空间中数据
					{
						tc.UnLock();
						Sleep(5);
						if (!started)//自动开始播放
							Start();
						while (!tc.Try()) { if (isClosing)return; }
						continue;
					}
					frames = bufferFrameCount / 2;
					if (buffer.GetUnreadSize() >= frames*SpeakingFormat.nBlockAlign && SUCCEEDED(hr = pRenderClient->GetBuffer(frames, &pdes)))//获取内存以填充
					{
						buffer.Read(pdes,frames*SpeakingFormat.nBlockAlign);//从缓存读取数据到指定区域
						if (FAILED(hr = pRenderClient->ReleaseBuffer(frames, NULL)))goto End;
					}
					else
					{
						goto End;
					}
					if (buffer.GetUnreadSize() < frames*SpeakingFormat.nBlockAlign)break;
				}
			End:;
				isWriting = false;
				tc.UnLock();
				if (FAILED(hr))Throw<PSoundException >(hr);
			}

		}
	}
}
#endif
